﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace MapleLeaf.Client.Injector {
    public static class CLRInjector {

        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibraryEx(string path, IntPtr file, int flag);


        [DllImport("kernel32.dll")]
        private static extern IntPtr GetProcAddress(IntPtr lib, string funcName);


        [DllImport("user32.dll")]
        private static extern int SetWindowsHookEx(int idHook, IntPtr lpfn, IntPtr hInstance, int threadId);

        [DllImport("user32.dll")]
        private static extern bool PostThreadMessage(int threadId, uint msg, uint wParam, uint lParam);

        public static void Inject(string target, string module, string assembly, string method, string argv) {
            IEnumerable<Process> enumerable = Process.GetProcesses().Where(delegate (Process p) {
                bool flag = false;
                try {
                    ProcessModuleCollection modules = p.Modules;
                    for (int i = 0; i < modules.Count; i++) {
                        string fileName = modules[i].FileName;
                        if (!flag) {
                            if (fileName.Equals(target, StringComparison.OrdinalIgnoreCase)) {
                                flag = true;
                            }
                        }
                        else if (fileName.Equals(HostingFile, StringComparison.OrdinalIgnoreCase)) {
                            flag = false;
                            break;
                        }
                    }
                }
                catch {
                }
                return flag;
            });
            foreach (Process process in enumerable) {
                Inject(process.Threads[0].Id, Path.GetFullPath(module), assembly, method, argv);
            }
        }


        public static void Inject(int thread_id, string module, string assembly, string method, string argv) {
            int num = SetWindowsHookEx(3, HostingMethod.Value, HostingModule.Value, thread_id);
            if (num != 0) {
                _ = PostThreadMessage(thread_id, 0U, 0U, 0U);
                PostMessage(thread_id, CLRHostingMessage.Assembly, module);
                PostMessage(thread_id, CLRHostingMessage.Class, assembly);
                PostMessage(thread_id, CLRHostingMessage.Methode, method);
                PostMessage(thread_id, CLRHostingMessage.Argv, argv);
                PostMessage(thread_id, CLRHostingMessage.Finish, 0, num);
            }
        }


        private static void PostMessage(int thread_id, CLRHostingMessage msg, string str) {
            if (!string.IsNullOrEmpty(str)) {
                for (int i = 0; i < str.Length; i++) {
                    PostMessage(thread_id, msg, i, str[i]);
                }
            }
        }

        private static bool PostMessage(int thread_id, CLRHostingMessage msg, int i, int v) {
            return PostThreadMessage(thread_id, 65505U, (uint)(msg | (CLRHostingMessage)(ushort)(i & 65535)), (uint)v);
        }


        static CLRInjector() {
            HostingFile = Path.GetFullPath("CLRHosting.dll");
            HostingModule = new Lazy<IntPtr>(() => LoadLibraryEx(HostingFile, IntPtr.Zero, 1));
            HostingMethod = new Lazy<IntPtr>(() => GetProcAddress(HostingModule.Value, "CallHost"));
        }

        private static readonly string HostingFile;


        private static readonly Lazy<IntPtr> HostingModule;


        private static readonly Lazy<IntPtr> HostingMethod;


        private enum CLRHostingMessage : uint {

            Finish,

            Assembly = 65536U,

            Class = 131072U,

            Methode = 196608U,

            Argv = 262144U
        }
    }
}
